`timescale 1ns / 1ps
module Toggle_port(
/*********NAND flash Toggle mode Port*********/
    output      WE_s,   
    output      RE_s,   
    output      CLE,   
    output      ALE,   
    inout[7:0]  DQ,     
    inout       DQS,    
    input       RB,     
/*********************************************/
    input       clkX2,
    input       dclk,
    input       rst_n,

    input       en,         //port module enable
    input[2:0]  Cmd,  //
    input[7:0]  InputVal,       //
    output      IV_En,
    output[7:0] ReadVal,
    output      RV_En,
    input[31:0] RWDataNum,
    input[31:0] RWDataCounter
);

wire[7:0] I_DQ;               
wire      I_DQS;
wire      clk_m;
wire      dqs_m;
wire      ready_r;
wire[7:0] O_DQ;
wire      we_s_m;
wire      re_s_m;

reg[7:0]  O_DQ_r0;
reg[7:0]  O_DQ_r1;
reg[7:0]  val_r;
reg      dv_r;
reg      dqs_en;
reg      clk_en;
reg      ale_r;
reg      cle_r;
reg      read_en;
reg      dqs_en;
reg      dataIn_rdy;
reg      we_s_en;
reg      re_s_en;
reg      CAorD;
reg      re_s_auto;
reg      re_s_r;


assign O_DQ = CAorD ? O_DQ_r0 : O_DQ_r1;
assign IV_En = en ? ~ready_r : 1'bz;
assign ready_r = curState == WriteData ? ~dataIn_rdy : dclk;
assign clk_m = en & clkX2;
assign WE_s = en ? we_s_m : 1'bz;
assign we_s_m = ~(we_s_en & dclk);
assign RE_s = en ? re_s_m : 1'bz;
assign re_s_m = re_s_auto ? ~(re_s_en & dclk) : re_s_r;
assign ALE = en ? ale_r : 1'bz;
assign CLE = en ? cle_r : 1'bz;
assign I_DQ = DQ;
assign DQ = read_en ? 8'hzz : O_DQ;
assign I_DQS = DQS;
assign DQS = read_en ? 1'hz : dqs_m;
assign dqs_m = dqs_en ? dclk : 1'b0;
assign ReadVal = en ? val_r : 8'hzz;
assign RV_En = en ? dv_r : 1'bz;

parameter State_command =   3'b000;
parameter State_adderss =   3'b001;
parameter State_WriteData = 3'b010;
parameter State_ReadData  = 3'b011;
parameter State_Idle =      3'b100;
parameter State_WriteWait = 3'b111;

parameter Idle =      4'b1111;
parameter WriteWait = 4'b0000;
parameter Command =   4'b0001;
parameter WriteData = 4'b0010;
parameter ReadData =  4'b0100;
parameter Address =   4'b1000;

parameter dqsStartDelay = 8'h02;
parameter resStartDelay = 4'h1;

reg[3:0]  curState; 

always @(posedge clk_m or negedge rst_n) begin
    if(rst_n != 1'b0) begin
        case (Cmd)
            State_command   : begin
                curState <= Command;
                read_en <=  1'b0;
                CAorD <=    1'b1;
            end
            State_adderss   : begin
                curState <= Address;
                read_en <=  1'b0;
                CAorD <=    1'b1;
            end
            State_ReadData  : begin
                curState <= ReadData;
                read_en <=  1'b1;
                CAorD <=    CAorD;
            end
            State_WriteData : begin
                curState <= WriteData;
                read_en <=  1'b0;
                CAorD <=    1'b0;
            end
            State_Idle      : begin
                curState <= Idle;
                read_en <=  1'b0;
                CAorD <=    CAorD;
            end
            default: begin
                curState <= Idle;
                read_en <=  1'b0;
                CAorD <=    CAorD;
            end
        endcase
    end else begin
        curState <= Idle;
        read_en <=  1'b1;
        CAorD <=    CAorD;
    end
end

always @(posedge clk_m or negedge rst_n) begin
    
end

always @(posedge dclk or negedge rst_n) begin
    if (rst_n) begin
        case (curState)
           Address : begin
                ale_r <=   1'b1;
                cle_r <=   1'b0;
                we_s_en <= 1'b1;
           end
           Command : begin
                ale_r <=   1'b0;
                cle_r <=   1'b1;
                we_s_en <= 1'b1;
           end
           ReadData : begin
                ale_r <=   1'b0;
                cle_r <=   1'b0; 
                we_s_en <= 1'b0;
           end
           WriteData : begin
                ale_r <=   1'b0;
                cle_r <=   1'b0;
                we_s_en <= 1'b0;
           end
           Idle : begin
                ale_r <=   1'b0;
                cle_r <=   1'b0;
                we_s_en <= 1'b0;
           end
            default : begin
                ale_r <= ale_r;
                cle_r <= cle_r;
                we_s_en <= we_s_en;
            end 
        endcase
    end else begin
        ale_r <= 1'b0;
        cle_r <= 1'b0;
        we_s_en <= 1'b0;
    end
end

always @(negedge clk_m or negedge rst_n) begin
    if (rst_n) begin
        if (dclk & (curState == Address | curState == Command)) begin
            O_DQ_r0 <= InputVal;
        end else begin
            O_DQ_r0 <= O_DQ_r0;
        end
    end else begin
        O_DQ_r0 <= O_DQ_r0;
    end
end

reg[3:0] dqsCounter;
always @(posedge clk_m or negedge rst_n) begin
    if (rst_n) begin
        if (curState == WriteData) begin
            if (dqsCounter == dqsStartDelay & RWDataCounter != RWDataNum) begin
                dqsCounter <= dqsCounter;
                dataIn_rdy <= 1'b1;
                dqs_en     <= 1'b1;    
            end else begin
                dqsCounter <= dqsCounter + 1'b1;
                dataIn_rdy <= 1'b0;
                dqs_en     <= 1'b0;    
            end
        end else begin
            dqsCounter <= 4'h0;
            dataIn_rdy <= 1'b0;
            dqs_en     <= 1'b0;    
        end
    end else begin
        dqsCounter <= 4'h0;
        dataIn_rdy <= 1'b0;
        dqs_en     <= 1'b0;    
    end
end

always @(negedge clk_m or negedge rst_n) begin
    if (rst_n) begin
        if (IV_En & curState == WriteData) begin
            O_DQ_r1 <= InputVal;
        end else begin
            O_DQ_r1 <= O_DQ_r1;
        end
    end else begin
        O_DQ_r1 <= O_DQ_r1;
    end
end

reg[3:0] resCounter;
always @(posedge dclk or negedge rst_n) begin
    if (rst_n) begin
        if (curState == ReadData) begin
            if (resCounter == resStartDelay) begin
                re_s_en <= 1'b1;
            end else begin
                re_s_en <= 1'b0;
                resCounter <= resCounter + 1'b1;
            end
        end else begin
            re_s_en <= 1'b0;
            resCounter <= 4'h0;
        end
    end else begin
        re_s_en <= 1'b0;
        resCounter <= 4'h0;
    end
end

always @(posedge dclk or negedge rst_n) begin
    if (rst_n) begin
        if (curState == ReadData) begin
            if (RWDataCounter == RWDataNum - 8'h02) begin
                re_s_r <= 1'b0;
                re_s_auto <= 1'b0;
            end if (RWDataCounter == RWDataNum) begin
                re_s_r <= 1'b1;
                re_s_auto <= 1'b1;
            end else begin
                re_s_r <= re_s_r;
                re_s_auto <= re_s_auto;
            end
        end else begin
            re_s_auto <= 1'b1;
            re_s_r <= 1'b0;
        end
    end else begin
        re_s_auto <= 1'b1;
        re_s_r <= 1'b0;
    end
end

reg readStart;
always @(posedge I_DQS or negedge rst_n) begin
    if (rst_n) begin// & I_DQS == 1'b1
        if (curState == ReadData) begin
            readStart <= 1'b1;
        end else begin
            readStart <= 1'b0;           
        end
    end else begin
        readStart <= 1'b0;
    end
end

always @(negedge clk_m or negedge rst_n) begin
    if (rst_n) begin
        if (curState == ReadData & readStart) begin
            val_r <= I_DQ;
            dv_r <= 1'b1;
        end else begin
            val_r <= val_r;
            dv_r <= 1'b0;
        end
    end else begin
        val_r <= val_r;
        dv_r <= 1'b0;
    end
end

endmodule // Toggle_port